home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 7 / BBS in a Box - Macintosh - Volume VII (BBS in a Box) (January 1993).iso / Files / Tele / C / Comet2.1.3.cpt / Comet / timer.c < prev    next >
C/C++ Source or Header  |  1990-07-26  |  12KB  |  504 lines

  1. /*  Copyright 1984 by the Massachusetts Institute of Technology  */
  2.  
  3. /*
  4.     Copyright Cornell University 1986.  All rights are reserved.
  5.     As of 4/10/86:
  6.     This source file may have no changes from the M.I.T original
  7.     other than this notice; but it has been tested as part of 
  8.     Cornell's Aztec-C port.  See notice.h
  9. */
  10.  
  11. /*  See permission and disclaimer notice in file "notice.h"  */
  12.  
  13. /*  Addition of tm_tset and tm_mset, 12/83. <J.H. Saltzer>  */
  14. /* 8/19/86 kevin increased TIMERSTACK to 4000 so Alert() in timer 
  15.     upcall won't cause crash */
  16. /* 11/19/86 kevin decreased stack to 2000 */
  17. /* 8/21/87 kevin decreased stack to 1000 */
  18. /* 10/16/87 kevin changed q_adda to q_addb so timers really are
  19.     in timeout order */
  20. /* 10/16/87 kevin made timer q traversals more intelligible */
  21. /* 3/16/88 kevin removed register declarations which caused bugs */
  22. /* 3/16/88 kevin made sure tnonce never == 0 */
  23. /* 7/24/89 kevin rewrote for MacTCP, added alarm.c */
  24.  
  25. #include    <notice.h>
  26.  
  27. /* This file contains the routines which make up the timer task and
  28.  * its associated subroutines for setting and clearing timers.  The
  29.  * timer task manages a queue of timers, each of which specifies a
  30.  * subroutine to be called (in the context of the timer task) when the
  31.  * timer goes off.
  32.  * The following routines are included in this package:
  33.  *    tm_set        set a timer to fire after number of seconds
  34.  *    tm_mset        set a timer, argument in milliseconds
  35.  *    tm_tset        set a timer, argument in clock ticks
  36.  *    tm_reset    reset a timer to go off at a different time
  37.  *    tm_clear    clear a previously set timer
  38.  *    tm_main        the main routine of the timer task
  39.  *    tm_init        init the timer system
  40.  *    tm_off        turn off timer interrupts
  41.  *    tm_on        turn timer interrupts back on
  42.  */
  43.  
  44. #include <q.h>
  45. #include <timer.h>
  46. #include <retrace.h>
  47. #include <memory.h>
  48.  
  49. #include    <emdefs.h>
  50.  
  51. extern char *malloc();
  52.  
  53. #define    TIMERHIWATER    30        /* number of free timers to keep */
  54.  
  55. time_q    tm_queue;                /* queue of active timers */
  56. time_q    tm_freeq;                /* queue of free timers */
  57. nonce    tnonce;                    /* timer nonce generator */
  58. char timertripped = FALSE;        /* the timer requires service */
  59.  
  60. unsigned long cticks = 0L;
  61. unsigned long next_alarm = 0L;
  62. unsigned long oldalarm;
  63. unsigned TIMERDEBUG = 0;
  64.  
  65. /* set timer in seconds  */
  66.  
  67. tm_set(nsecs, subr, arg, tm)
  68. int nsecs;                    /* timer expiration time, in secs */
  69. int    (*subr)();                /* subroutine to call on expiration */
  70. char *arg;                    /* arg to pass to subr. */
  71. timer *tm;                    /* place to return timer id */
  72. {    
  73.     tm_tset(nsecs*TPS, subr, arg, tm);
  74. }
  75.  
  76.  
  77. /* set timer in milliseconds  */
  78.  
  79. tm_mset(msecs, subr, arg, tm)
  80. long msecs;                /* timer expiration time, in msecs */
  81. int    (*subr)();            /* subroutine to call on expiration */
  82. char *arg;                /* arg to pass to subr. */
  83. timer *tm;        /* place to return timer id */
  84. {
  85.     tm_tset((int)((msecs*TPS)/1000), subr, arg, tm);
  86. }
  87.  
  88.  
  89. /* Set a timer to go off after nticks clock ticks.  When the timer 
  90.    goes off, call the specified subroutine with the specified 
  91.    argument flag.  This routine enqueues the timer and, 
  92.    if it is the first timer 
  93.    on the queue, sets an flag to call the timer task.
  94. */
  95.  
  96. tm_tset(nticks, subr, arg, tm)
  97. int nticks;            /* timer expiration time */
  98. int    (*subr)();                /* subroutine to call on expiration */
  99. char *arg;                    /* arg to pass to subr. */
  100. timer *tm;            /* place to return timer id */
  101. {
  102.     timer    *tmp;        /* temp for chaining */
  103.     queue *qp;
  104.  
  105. #ifdef    DEBUG
  106.     if (TIMERDEBUG) {
  107.        printf("TM_SET:  %8X set for %u ticks\n", tm, nticks);
  108.     }
  109. #endif
  110.     
  111.     if (tm == (timer *) NULL)
  112.         return(-1);
  113.         
  114.     q_del(&tm_queue, tm);            /*  make sure not already queued. */
  115.     tm->tm_elt.qe_next = NULL;        /* no next element */
  116.     tm->tm_time = cticks + nticks;    /* timer expiration time */
  117.         /* TODO should check for wrap */
  118.     if (!++tnonce)
  119.         /* make sure it skips zero */
  120.         ++tnonce;
  121.     tm->tm_nonce = tnonce;        /* nonce for this timer */
  122.     tm->tm_subr = subr;                /* subroutine to call */
  123.     tm->tm_arg = arg;                /* argument to pass */
  124.  
  125. /* add to queue in timeout order */
  126.     for (tmp = tm_queue.tmq_head;    
  127.             tmp != NULL && tmp->tm_time <= tm->tm_time;
  128.             tmp = (timer *) tmp->tm_elt.qe_next)
  129.         ;
  130.  
  131.     qp = (queue *) &tm_queue;
  132.     q_addb(qp, (q_elt) tmp, (q_elt) tm);
  133.     if (tm_queue.tmq_head == tm)     /* first elt in queue? */
  134.         set_alarm(nticks);            /* yes, wake up timer task then */
  135.  
  136. }    
  137.  
  138.  
  139. /* Reset a (running) timer to go off in nsecs seconds
  140.    instead of at the time it is currently set for.  If in fact the
  141.    timer is not already set, return FALSE; otherwise return TRUE.
  142.    Does not modify the upcall in the timer.
  143. */
  144.  
  145. tm_reset(nsecs, tm)
  146. int    nsecs;                    /* timer expiration time in seconds */
  147. timer *tm;            /* timer id to reset*/
  148. {
  149.  
  150.     timer *tmp;        /* temp for chaining */
  151.     queue *qp;
  152.     
  153.     if (tm->tm_nonce == 0 || ! q_del(&tm_queue, tm)) {
  154. #ifdef    DEBUG
  155.         if (TIMERDEBUG)
  156.             printf("TM_RST:  %8X already expired.\n", tm);
  157. #endif
  158.         return(FALSE);        /* timer expired, give up */
  159.     }
  160.  
  161. #ifdef    DEBUG
  162.     if (TIMERDEBUG)
  163.         printf("TM_RST:  %8X reset for %u seconds.\n", tm, nsecs);
  164. #endif
  165.  
  166.     
  167.     tm->tm_elt.qe_next = NULL;            /* no next element */
  168.     tm->tm_time = cticks + nsecs * TPS;    /* timer expiration time */
  169.     
  170.     for (tmp = tm_queue.tmq_head;
  171.             tmp != NULL && tmp->tm_time <= tm->tm_time;
  172.             tmp = (timer *) tmp->tm_elt.qe_next)
  173.         /* add to queue in timeout order */
  174.         ;
  175.  
  176.     qp = (queue *)&tm_queue;
  177.     q_addb(qp, (q_elt)tmp, (q_elt)tm);
  178.     
  179.     if (tm_queue.tmq_head == tm)    /* first elt in queue? */
  180.         set_alarm(nsecs*TPS);        /* yes, wake up timer task then */
  181.  
  182.     return(TRUE);
  183. }    
  184.  
  185.  
  186.  
  187. /* Clear the timer specified by the passed timer identifier.  The 
  188.    timer identifier gives a pointer to the timer to be cleared.
  189.    Free the timer's storage
  190.    (into the free list up to TIMERHIWATER elements).
  191.    If it was the only timer on the queue, reset the pending alarm.
  192.    Returns FALSE if the specified alarm was not found in the queue
  193.    TRUE otherwise.
  194. */
  195.  
  196. tm_clear(tm)
  197. timer *tm;            /* identifier of timer to clear */
  198. {
  199.     
  200.     if (tm->tm_nonce == 0) {
  201. #ifdef    DEBUG
  202.         if (TIMERDEBUG)
  203.             printf("TM_CLR:  %8X already expired.\n", tm);
  204. #endif
  205.         return FALSE;
  206.     }
  207.  
  208. #ifdef    DEBUG
  209.     if (TIMERDEBUG)
  210.         printf("TM_CLR:  %8X\n", tm);
  211. #endif
  212.  
  213.     tm->tm_nonce = 0;
  214.     if(!q_del(&tm_queue, tm)) 
  215.         return(FALSE);                /* timer expired, give up */
  216.         
  217.     if (tm_queue.tmq_head == NULL)    /* last elt on queue? */
  218.         set_alarm(-1);                /* yes, turn off alarm */
  219.         
  220.     return(TRUE);                    /* success */
  221.  
  222. }    
  223.  
  224.  
  225. /* This routine forms the body of the timer management task.  Its
  226.    job is simply to dequeue expired timers from the timer queue
  227.    and call the subroutine specified therein, passing it the argument
  228.    specified therein.  
  229.    Note that this task needs the alarm signal, so all timer
  230.    management in the process must be via this task - no one else
  231.    may use the alarm() calls!
  232. */
  233.  
  234. tm_main()
  235. {
  236.     timer    *tm_tmp;        /* temp for holding timer */
  237.     
  238.     timertripped = FALSE;
  239.     while ((tm_tmp = tm_queue.tmq_head) != NULL 
  240.             && cticks >= tm_tmp->tm_time)  {
  241.         /* for all expired timers */
  242.  
  243.         tm_tmp = (timer *) q_deq(&tm_queue);   /* dequeue it */
  244.  
  245.         /* if the timer is expired, ignore it */
  246.         if (tm_tmp->tm_nonce == 0) {
  247. #ifdef    DEBUG
  248.             if (TIMERDEBUG)
  249.                 printf(" TIMER: %8X already fired.\n", tm_tmp);
  250. #endif
  251.                  continue;
  252.         }
  253.  
  254. #ifdef    DEBUG
  255.         if (TIMERDEBUG)
  256.             printf(" TIMER: %8X firing.\n", tm_tmp);
  257. #endif
  258.  
  259.         tm_tmp->tm_nonce = 0;                      /* show timer expired... */
  260.         (*tm_tmp->tm_subr)(tm_tmp->tm_arg);     /* call its routine */
  261.  
  262.     }
  263.     
  264.     if (tm_queue.tmq_head != NULL) {
  265.         /* there are outstanding timers, reset the alarm */
  266.         set_alarm((int)(tm_queue.tmq_head->tm_time - cticks)); 
  267.     }
  268. }    
  269.  
  270.  
  271. /* Initialize the timer package.  Set up the alarm routine tm_signal(). */
  272.  
  273. tm_init()
  274. {
  275.     extern int alarm_clnup();
  276.     
  277.     alarm_init();        /* set up out alarm function    */
  278.     exit_hook(alarm_clnup);        
  279. }
  280.  
  281.  
  282.  
  283. /* Turn off timer interrupts.  This is useful while writing data to
  284.    the terminal. */
  285.  
  286. tm_off()    
  287. {
  288.     oldalarm = next_alarm;
  289.     set_alarm(-1);
  290. }
  291.  
  292.  
  293. /* Turn timer interrupts back on.  This is done by running the 
  294.    timer task to process any events that went off while timer 
  295.    interrupts were disabled.  If anything else is queued, that task 
  296.    will set a new alarm. */
  297.    
  298. tm_on()    
  299. {    
  300.     tm_main();
  301. }
  302.  
  303. /*****/
  304.  
  305. timer *tm_alloc() 
  306. {
  307.  
  308.     /* Allocate a timer and return a pointer to it */
  309.  
  310.     timer *t;
  311.     if((t = (timer *)q_deq(&tm_freeq)) == NULL &&
  312.        (t = (timer *)malloc(sizeof(timer))) == NULL) return NULL;
  313.  
  314.     t->tm_elt.qe_next = NULL;
  315.     return t;
  316. }
  317.  
  318. /*****/
  319.  
  320. tm_free(t)
  321.     timer *t;
  322. {
  323.     /* Free up a timer. Returns true if successful, false otherwise */
  324.  
  325.     timer **tmp;
  326.     queue *qp;
  327.  
  328.     /* Check if the timer is enqueued */
  329.  
  330.     for(tmp = &tm_queue.tmq_head; *tmp != NULL;
  331.         tmp = (timer **)(((timer *)tmp)->tm_elt.qe_next))
  332.             if(*tmp == t) {
  333. #ifdef    DEBUG
  334.                 printf("Tried to free active timer.\n");
  335. #endif
  336.                 return FALSE;
  337.                 }
  338.  
  339.     if (tm_freeq.tmq_len < TIMERHIWATER) 
  340.     {
  341.         qp = (queue *)&tm_freeq;
  342.         q_addh(qp, (q_elt)t);
  343.     }
  344.     /*
  345.     else    
  346.        cfree(t);
  347.     */
  348.  
  349.     return TRUE;
  350. }
  351.  
  352. /*****/
  353.  
  354. tm_qdump()
  355. {
  356.     /* 
  357.        Dump some information about the timer queue to the display. Used 
  358.        for debugging. Takes a timer * as an argument and tells if it's 
  359.        in the queue.
  360.      */
  361.  
  362.     printf("Nonce = %u\n", tnonce);
  363.     printf("timer queue:\n");
  364.     printf("\thead %8X\ttail %8X\n", tm_queue.tmq_head,
  365.                             tm_queue.tmq_tail);
  366.     printf("\tlength %u\n", tm_queue.tmq_len);
  367. }
  368.  
  369. /*****/
  370.  
  371. tm_dump(t)
  372.    timer *t; 
  373. {
  374.     /*  For debugging, dump the interesting parts of a timer. */
  375.  
  376.     printf("dump of timer %8X:\n", t);
  377.     printf("  fire time is %8X, nonce is %u, next q element is %8X\n",
  378.             t->tm_time, t->tm_nonce, t->tm_elt.qe_next);
  379.  
  380. }
  381.  
  382.  
  383. /* from task/alarm.c */
  384.  
  385.  
  386. /*
  387.     Copyright 1984 M.I.T.
  388.     Copyright Cornell University 1986.  All rights are reserved.
  389.  
  390.     As of 4/10/86:
  391.     This source file may have no changes from the M.I.T original
  392.     other than this notice; but it has been tested as part of 
  393.     Cornell's Aztec-C port.  See notice.h
  394.  
  395. */
  396.  
  397. /* 5/8/87 kevin modified to remove saveA5() and avoid a5 problems john discovered */
  398. /* 4/13/88 kevin modified for MultiFinder:  allocated VBL task in system heap */
  399.  
  400. #define vType 1
  401.  
  402. struct a5save {
  403.     char * mya5;    /* saves the a5 */
  404.     VBLTask mytask;
  405. } myvbl;
  406.  
  407. alarm_init()
  408. {
  409.     void signal();
  410.     short status;
  411.     THz    appzone;
  412.  
  413.     appzone = GetZone();
  414.     SetZone(SystemZone());
  415.         /* allocate the alarm in the system heap
  416.             so that it gets service even when MultiFinder 
  417.             switches out application */
  418.  
  419.     myvbl.mytask.qType = vType;
  420.     myvbl.mytask.vblAddr = signal;
  421.     myvbl.mytask.vblCount = 1;            /* call every tick */
  422.     myvbl.mytask.vblPhase = 0;
  423.  
  424.     myvbl.mya5 = (char *) (* (long *) 0x904);
  425.     status = VInstall(&myvbl.mytask);
  426.     SetZone(appzone);
  427.     if (status) {
  428.         error("Can't install retrace routine");
  429.         return(-1);
  430.     }
  431.     return(0);
  432. }
  433.  
  434. alarm_clnup()
  435. {
  436.     short status;
  437.     THz    appzone;
  438.  
  439.     appzone = GetZone();
  440.     SetZone(SystemZone());
  441.  
  442.     status = VRemove(&myvbl.mytask);
  443.     SetZone(appzone);
  444. }
  445.  
  446.  
  447. /* this routine is called by the VBL timer every tick */
  448.  
  449. void signal()
  450. {
  451. #asm
  452.     move.l    a5,-(a7)
  453.     movea.l    -4(a0),a5
  454.         ; move the a5 we saved earlier into a5
  455.     move.w    #1,10(a0)
  456.         ;    task->vblCount = 1; doesn't work on a Mac Portable!
  457. #endasm
  458.  
  459.     cticks++;
  460.     if (!cticks) {
  461.         /* we've wrapped around, reset times on q that have popped */
  462.         tm_wrap();
  463.         user_tmwrap();
  464.     }
  465.     if (next_alarm)
  466.         /* don't decrement if already zero */
  467.         if (--next_alarm == 0L)
  468.             timertripped = TRUE;
  469.  
  470.     ;    /* null statement so asm directive gets spotted! */
  471. #asm
  472.     move.l    (a7)+,a5
  473. #endasm
  474. }
  475.  
  476.  
  477. set_alarm(ntime)
  478. int ntime;
  479. {
  480.     if (ntime >= -1) {
  481.         /* ignore weird args */
  482.         next_alarm = ntime + 1;
  483.     }
  484. }
  485.  
  486.  
  487.  
  488. tm_wrap()
  489. {
  490.     timer *timerp;
  491.  
  492.     for (timerp = tm_queue.tmq_head; timerp != NULL;
  493.         timerp = (timer *) timerp->tm_elt.qe_next) {
  494.  
  495.         /* if the time value is large, reset it so the timer will service it */
  496.         if (timerp->tm_nonce) {
  497.             if (timerp->tm_time > 3000000)
  498.                 timerp->tm_time = 0;
  499.         }
  500.     }
  501. }
  502.  
  503.  
  504.